#include "Components/ComponentVitalStats.h"
#include "Components/ComponentSickness.h"
#include "Components/ComponentPlayer.h"
#include "Components/ComponentBody.h"
#include "Components/ComponentSleep.h"
#include "Components/ComponentCreature.h"
#include "Components/ComponentLevel.h"
#include "Components/ComponentCreatureModel.h"
#include "Components/ComponentHealth.h"
#include "Components/ComponentClothing.h"

#include "Terrain/Terrain.h"
#include "Managers/BlocksManager.h"
#include "Blocks/WaterBlock.h"

#include "Subsystems/SubsystemTimeOfDay.h"
#include "Subsystems/SubsystemGameInfo.h"
#include "Subsystems/SubsystemWeather.h"
#include "Subsystems/BlockBehaviors/SubsystemMetersBlockBehavior.h"

#include "Network/PackageManager.h"
#include "Network/Packages/ComponentVitalStatPackage.h"
#include "Player/PlayerData.h"

#include "Log.h"
#include "GameRandom.hpp"
#include "GameSingleton.h"
#include "GameRegistry.hpp"
#include "GameThreadPool.hpp"

namespace PocketSurvival
{
    void ComponentVitalStats::update(float dt)
    {
        SubsystemTimeOfDay *timeOfDay = GameSingleton::singletonPtr->getSingleton<SubsystemTimeOfDay>();
        SubsystemWeather *weather = GameSingleton::singletonPtr->getSingleton<SubsystemWeather>();
        SubsystemGameInfo *gameInfo = GameSingleton::singletonPtr->getSingleton<SubsystemGameInfo>();
        SubsystemMetersBlockBehavior *m_metersBlockBehavior = GameSingleton::singletonPtr->getSingleton<SubsystemMetersBlockBehavior>();

        ComponentBody *componentBody = componentPlayer->componentBody;
        ComponentClothing *componentClothing = componentPlayer->componentClothing;

        Vector3 position;
        componentBody->getPosition(position);

        // updateFood
        {
            Vector3 velocity;
            componentBody->getVelocity(velocity);

            Vector2 velocityXZ(velocity.posX, velocity.posZ);
            float num2 = componentBody->componentCreature->componentCreatureModel->getEyePosition().posY - position.posY;
            bool flag = componentBody->immersionDepth > num2;
            bool flag2 = componentBody->immersionFactor > 0.33f && componentPlayer->componentBody->standingOnValue == 0;
            bool flag3 = timeOfDay->periodicGameTimeEvent(240.0, 13.0) && false == componentPlayer->componentSickness->isSick();
            if (componentPlayer->playerData->gameMode != GameMode::Creative && gameInfo->areAdventureSurvivalMechanicsEnabled == true)
            {
                float hungerFactor = componentPlayer->componentLevel->getHungerFactor();
                float decFood = hungerFactor * dt / 2880.0f;
                if (flag2 || flag3)
                {
                    decFood += hungerFactor * dt * velocityXZ.lengthSquared() / 1440.0f;
                }
                else
                {
                    decFood += hungerFactor * dt * velocityXZ.lengthSquared() / 2880.0f;
                }
                if (velocity.posY > 0.0f)
                {
                    decFood += hungerFactor * dt / 1800.0f;
                }
                setFood(food - decFood);

                if (componentPlayer->componentSleep->isSleeping() == false)
                {
                    if (food <= 0.0f)
                    {
                        if (timeOfDay->periodicGameTimeEvent(30.0, 0.0))
                        {
                            componentPlayer->componentHealth->injure(0.05f, entt::null, false, "饿死了");
                        }
                    }
                }
            }
            else
            {
                setFood(0.9f);
            }
            if (timeOfDay->periodicGameTimeEvent(1.0, -0.01))
            {
                satiationMapMutex.lock();
                for (auto it = satiationMap.begin(); it != satiationMap.end(); ++it)
                {
                    it->second = MathUtils::Max<float>(it->second - 0.01 * dt, 0.0f);
                }
                satiationMapMutex.unlock();
            }
        }
        // updateStamina
        {
        }
        // updateSleep
        {
            // 睡眠功能暂时有问题
            /*
            bool isImmersion = componentBody->immersionFactor > 0.05f;
            if (componentPlayer->playerData->gameMode != GameMode::Creative && gameInfo->areAdventureSurvivalMechanicsEnabled == true)
            {
                if (componentPlayer->componentSleep->isSleeping())
                {
                    setSleep(sleep + 0.05f * dt);
                }
                else if (isImmersion == false)
                {
                    float amount = dt / 1800.0f;
                    setSleep(sleep - amount);

                    if (sleep <= 0.0f && componentPlayer->componentSleep->isSleeping())
                    {
                        componentPlayer->componentSleep->sleep(false);
                    }
                }
            }
            else
            {
                setSleep(0.9f);
            }
            */
           setSleep(0.9f);
        }
        // updateTemperature
        {
            bool flag = timeOfDay->periodicGameTimeEvent(300.0, 17.0);
            float num = componentClothing->insulation * MathUtils::Lerp(1.0f, 0.05f, MathUtils::Saturate(4.0f * wetness));
            if (componentPlayer->playerData->gameMode <= GameMode::Survival)
            {
                num = num * 1.5f + 1.0f;
            }
            // 计算环境温度
            // ...
            if (timeOfDay->periodicGameTimeEvent(2.0, 0))
            {
                int32_t x = Terrain::ToCell(position.posX);
                int32_t y = Terrain::ToCell(position.posY + 0.1f);
                int32_t z = Terrain::ToCell(position.posZ);
                m_metersBlockBehavior->calculateTemperature(x, y, z, 12.0f, num, environmentTemperature, environmentTemperatureFlux);

                // Log::info(fmt::format("temperature: {}, environmentTemperature: {}, environmentTemperatureFlux: {}", temperature, environmentTemperature, environmentTemperatureFlux));
            }
            if (componentPlayer->playerData->gameMode != GameMode::Creative && gameInfo->areAdventureSurvivalMechanicsEnabled == true)
            {
                float num2 = environmentTemperature - temperature;
                float num3 = 0.01f + 0.005f * environmentTemperatureFlux;
                setTemperature(temperature + MathUtils::Saturate(num3 * dt) * num2);
            }
            else
            {
                setTemperature(12.0f);
            }
            if (temperature <= 0.0f)
            {
                componentPlayer->componentHealth->injure(1.0f, entt::null, false, "被冻死了");
            }
            else if (temperature <= 3.0f)
            {
                if (timeOfDay->periodicGameTimeEvent(10.0, 0.0))
                {
                    componentPlayer->componentHealth->injure(0.05f, entt::null, false, "被冻死了");
                }
            }
            if (temperature >= 24.0f)
            {
                if (timeOfDay->periodicGameTimeEvent(10.0, 0.0))
                {
                    componentPlayer->componentHealth->injure(0.05f, entt::null, false, "被热死了");
                }
            }
        }
        // updateWetness
        {
            if (componentBody->immersionFactor > 0.2f && componentBody->immersionFluidBlock->isBlock<WaterBlock *>() == true)
            {
                float num = 2.0f * componentPlayer->componentBody->immersionFactor;
                setWetness(wetness + MathUtils::Saturate(3.0f * dt) * (num - wetness));
            }
            int32_t x = Terrain::ToCell(position.posX);
            int32_t num2 = Terrain::ToCell(position.posY + 0.1f);
            int32_t z = Terrain::ToCell(position.posZ);

            PrecipitationShaftInfo precipitationShaftInfo = weather->getPrecipitationShaftInfo(x, z);
            if (num2 >= precipitationShaftInfo.YLimit && precipitationShaftInfo.Type == PrecipitationType::Rain)
            {
                setWetness(wetness + 0.05f * precipitationShaftInfo.Intensity * dt);
            }

            float num3 = 180.0f;
            if (environmentTemperature > 8.0f)
            {
                num3 = 120.0f;
            }
            if (environmentTemperature > 16.0f)
            {
                num3 = 60.0f;
            }
            if (environmentTemperature > 24.0f)
            {
                num3 = 30.0f;
            }
            setWetness(wetness - (dt / num3));
        }

        if (timeOfDay->periodicGameTimeEvent(1.0, 0.5))
        {
            std::shared_ptr<IPackage> package = std::make_shared<ComponentVitalStatPackage>(this);
            package->sendType = PackageSendType::TARGET;
            package->client = componentPlayer->playerData->client;
            GameSingleton::packageManager->addSendPackageToQueue(package);
        }
    }


    bool ComponentVitalStats::eat(IInventory *inventory, int32_t slotIndex, int32_t value)
    {
        int32_t cellContents = Terrain::ExtractContents(value);
        Block *eatObj = GameSingleton::blocksManager->blocks[cellContents];
        if (eatObj == nullptr)
        {
            return false;
        }
        float num2 = eatObj->getNutritionalValue(value);
        float sicknessProbability = eatObj->getSicknessProbability(value);
        if (num2 > 0.0f)
        {
            if (food > 0.98f)
            {
                return false;
            }
            if (componentPlayer->componentSickness->isSick())
            {
                if (sicknessProbability > 0.0f)
                {
                    return false;
                }
                num2 *= 0.75f;
            }
            setFood(food + num2);

            satiationMapMutex.lock();
            auto it = satiationMap.find(cellContents);
            float value2 = 0.0f;
            if (it != satiationMap.end())
            {
                value2 = it->second;
            }
            value2 += MathUtils::Max<float>(num2, 0.5f);
            satiationMap[cellContents] = value2;
            satiationMapMutex.unlock();

            if (componentPlayer->componentSickness->isSick())
            {
                componentPlayer->componentSickness->nauseaEffect();
            }

            if (GameSingleton::gameRandom->getBool(sicknessProbability) || value2 > 3.5f)
            {
                componentPlayer->componentSickness->startSickness();
            }
            return true;
        }
        return false;
    }

    void ComponentVitalStats::applyDensityModifier(float modifier)
    {
        float num = modifier - densityModifierApplied;
        if (num != 0.0f)
        {
            densityModifierApplied = modifier;
            componentPlayer->componentBody->density += num;
        }
    }

    static const std::string componentName = "VitalStats";
    const std::string &ComponentVitalStats::getName() const
    {
        return componentName;
    }
    const std::string &ComponentVitalStats::GetName()
    {
        return componentName;
    }
    void ComponentVitalStats::load(const nlohmann::json &jsonData)
    {
        componentPlayer = GameSingleton::gameRegistry->get<ComponentPlayer *>(entity);

        food = jsonData["Food"].get<float>();
        stamina = jsonData["Stamina"].get<float>();
        sleep = jsonData["Sleep"].get<float>();
        temperature = jsonData["Temperature"].get<float>();
        wetness = jsonData["Wetness"].get<float>();

        for (const nlohmann::json &item : jsonData["Satiation"])
        {
            int32_t key = item[0].get<int32_t>();
            float value = item[1].get<float>();
            satiationMap[key] = value;
        }

        environmentTemperature = temperature;
    }

    void ComponentVitalStats::save(nlohmann::json &jsonData)
    {
        jsonData["Food"] = food;
        jsonData["Stamina"] = stamina;
        jsonData["Sleep"] = sleep;
        jsonData["Temperature"] = temperature;
        jsonData["Wetness"] = wetness;
        nlohmann::json satiationJson = nlohmann::json::array();
        for (const auto &pair : satiationMap)
        {
            nlohmann::json item = nlohmann::json::array();
            item.push_back(pair.first);
            item.push_back(pair.second);
            satiationJson.push_back(item);
        }
        jsonData["Satiation"] = satiationJson;
    }

    void ComponentVitalStats::saveToMsgJson(nlohmann::json &jsonData)
    {
        MSG_VALUE_SET(jsonData, "Food", "float", food);
        MSG_VALUE_SET(jsonData, "Stamina", "float", stamina);
        MSG_VALUE_SET(jsonData, "Sleep", "float", sleep);
        MSG_VALUE_SET(jsonData, "Temperature", "float", temperature);
        MSG_VALUE_SET(jsonData, "Wetness", "float", wetness);
        jsonData["Satiation"] = nlohmann::json::object();
    }
} // namespace PocketSurvival
